package main

import (
	"bytes"
	"fmt"
	"log"
	"reflect"
	"strings"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
)

func main() {
	path := "/Users/liruopeng/Downloads/test.pcap"
	handler, err := pcap.OpenOffline(path)
	if err != nil {
		log.Fatal(err)
	}
	defer handler.Close()
	// fmt.Println(handler.LinkType())

	packetSource := gopacket.NewPacketSource(handler, handler.LinkType())

	for packet := range packetSource.Packets() {
		fmt.Println(GetPacketInfo(packet))
	}
}

func printPacketInfo(packet gopacket.Packet) {
	// Let's see if the packet is an ethernet packet
	ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
	fmt.Println(packet.Layers())
	fmt.Println("layers.LayerTypeEthernet=", layers.LayerTypeEthernet)
	fmt.Println(reflect.TypeOf(layers.LayerTypeEthernet))
	fmt.Println(reflect.TypeOf(ethernetLayer))

	if ethernetLayer != nil {
		fmt.Println("Ethernet layer detected.")
		ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
		fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
		fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
		// Ethernet type is typically IPv4 but could be ARP or other
		fmt.Println("Ethernet type: ", ethernetPacket.EthernetType)
		fmt.Println()
	}

	// Let's see if the packet is IP (even though the ether type told us)
	ipLayer := packet.Layer(layers.LayerTypeIPv4)
	if ipLayer != nil {
		fmt.Println("IPv4 layer detected.")
		ip, _ := ipLayer.(*layers.IPv4)

		// IP layer variables:
		// Version (Either 4 or 6)
		// IHL (IP Header Length in 32-bit words)
		// TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
		// Checksum, SrcIP, DstIP
		fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP)
		fmt.Println("Protocol: ", ip.Protocol)
		fmt.Println()
	}

	// Let's see if the packet is TCP
	tcpLayer := packet.Layer(layers.LayerTypeTCP)
	if tcpLayer != nil {
		fmt.Println("TCP layer detected.")
		tcp, some := tcpLayer.(*layers.TCP)
		// tcp := tcpLayer
		fmt.Println(reflect.TypeOf(tcp), reflect.TypeOf(tcpLayer))
		// fmt.Println(tcpLayer)
		fmt.Println("some=", some)

		// TCP layer variables:
		// SrcPort, DstPort, Seq, Ack, DataOffset, Window, Checksum, Urgent
		// Bool flags: FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS
		// fmt.Printf("From port %d to %d\n", tcpLayer.SrcPort, tcpLayer.DstPort)
		fmt.Printf("From port %d to %d\n", tcp.SrcPort, tcp.DstPort)
		fmt.Println("Sequence number: ", tcp.Seq)
		fmt.Println()
	}

	// Iterate over all layers, printing out each layer type
	fmt.Println("All packet layers:")
	for _, layer := range packet.Layers() {
		fmt.Println("- ", layer.LayerType())
	}

	// When iterating through packet.Layers() above,
	// if it lists Payload layer then that is the same as
	// this applicationLayer. applicationLayer contains the payload
	applicationLayer := packet.ApplicationLayer()
	if applicationLayer != nil {
		fmt.Println("Application layer/Payload found.")
		fmt.Printf("%s\n", applicationLayer.Payload())

		// Search for a string inside the payload
		if strings.Contains(string(applicationLayer.Payload()), "HTTP") {
			fmt.Println("HTTP found!")
		}
	}

	// Check for errors
	if err := packet.ErrorLayer(); err != nil {
		fmt.Println("Error decoding some part of the packet:", err)
	}
}

func GetPacketInfo(packet gopacket.Packet) string {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "%d|", len(packet.Data()))
	fmt.Fprintf(&buf, "%d|", packet.Metadata().Timestamp.Unix())

	// 网络接口层
	ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
	ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
	fmt.Fprintf(&buf, "%s|", ethernetPacket.SrcMAC)
	fmt.Fprintf(&buf, "%s|", ethernetPacket.DstMAC)
	fmt.Fprintf(&buf, "%s|", ethernetPacket.EthernetType)

	// 网络层
	if ethernetPacket.EthernetType.String() == "IPv4" {
		ipLayer := packet.Layer(layers.LayerTypeIPv4)
		ip, _ := ipLayer.(*layers.IPv4)
		fmt.Fprintf(&buf, "%d|", ip.IHL)
		fmt.Fprintf(&buf, "%d|", ip.TOS)
		fmt.Fprintf(&buf, "%d|", ip.Id)
		fmt.Fprintf(&buf, "%s|", ip.Flags)
		fmt.Fprintf(&buf, "%d|", ip.FragOffset)
		fmt.Fprintf(&buf, "%d|", ip.TTL)
		fmt.Fprintf(&buf, "%s|", ip.SrcIP)
		fmt.Fprintf(&buf, "%s|", ip.DstIP)
		fmt.Fprintf(&buf, "%s|", ip.NextLayerType())
	}

	// 传输层
	tcpLayer := packet.Layer(layers.LayerTypeTCP)
	if tcpLayer != nil {
		tcp, _ := tcpLayer.(*layers.TCP)
		fmt.Fprintf(&buf, "%s|", tcp.SrcPort)
		fmt.Fprintf(&buf, "%s|", tcp.DstPort)
		fmt.Fprintf(&buf, "%d|", tcp.Seq)
		fmt.Fprintf(&buf, "%d|", tcp.Ack)
		fmt.Fprintf(&buf, "%t|", tcp.FIN)
		fmt.Fprintf(&buf, "%t|", tcp.SYN)
		fmt.Fprintf(&buf, "%t|", tcp.RST)
		fmt.Fprintf(&buf, "%t|", tcp.PSH)
		fmt.Fprintf(&buf, "%t|", tcp.ACK)
		fmt.Fprintf(&buf, "%t|", tcp.URG)
		fmt.Fprintf(&buf, "%t|", tcp.ECE)
		fmt.Fprintf(&buf, "%t|", tcp.CWR)
		fmt.Fprintf(&buf, "%t|", tcp.NS)
		fmt.Fprintf(&buf, "%d|", tcp.Window)
		fmt.Fprintf(&buf, "%d", tcp.Checksum)

		fmt.Println("FIN=%t|", tcp.FIN)
		fmt.Println("SYN=%t|", tcp.SYN)
		fmt.Println("RST=%t|", tcp.RST)
		fmt.Println("PSH=%t|", tcp.PSH)
		fmt.Println("ACK=%t|", tcp.ACK)
		fmt.Println("URG=%t|", tcp.URG)
		fmt.Println("ECE=%t|", tcp.ECE)
	}
	return buf.String()

}
